﻿// FindLargeFolderCPP.cpp: 定义应用程序的入口点。
//

#include "FindLargeFolderCPP.h"
#include "FolderSize.h"

using namespace std;

// 获取文件大小
unsigned long long getFileSize(const std::string &filePath) {
    struct stat st;
    if (stat(filePath.c_str(), &st) == 0) {
        return st.st_size;
    } else {
        std::cerr << "Error getting file size for " << filePath << std::endl;
        return 0;
    }
}

uintmax_t calcDirSize(FolderSize *folderSize) {
    if ((*folderSize).sizeAvailable) {
        return (*folderSize).size;
    }
    uintmax_t size = 0;
    try {
        for (FolderSize *subFolder: *((*folderSize).subFolders)) {
            if ((*subFolder).sizeAvailable) {
                size += (*subFolder).size;
            } else {
                size += calcDirSize(subFolder);
            }
        }
        DIR *dir;
        struct dirent *ent;
        if ((dir = opendir((*folderSize).path.c_str())) != nullptr) {
            while ((ent = readdir(dir)) != nullptr) {
                std::string path = (*folderSize).path + "/" + ent->d_name;
                if (ent->d_type == DT_REG) {
                    size += getFileSize(path);
                }
            }
            closedir(dir);
        } else {
            std::cerr << "Unable to open directory: " << (*folderSize).path << std::endl;
        }
        (*folderSize).size = size;
        (*folderSize).sizeAvailable = true;

    }
    catch (const system_error &e) {
        std::cerr << e.what() << std::endl;
    }
    return size;
}

bool compareDepth(FolderSize *fs1, FolderSize *fs2) {
    return (*fs1).depth > (*fs2).depth;
}

bool compareSize(FolderSize *fs1, FolderSize *fs2) {
    return (*fs1).size > (*fs2).size;
}

string presentSize(uintmax_t size) {
    if (size > 1024 * 1024 * 1024) {
        return to_string((double) size / (double) (1024 * 1024 * 1024)) + "GB";
    } else if (size > 1024 * 1024) {
        return to_string((double) size / (double) (1024 * 1024)) + "MB";
    } else if (size > 1024) {
        return to_string((double) size / (double) (1024)) + "KB";
    } else {
        return to_string(size) + "bytes";
    }
}

bool ends_with(const std::string& str, const std::string& suffix) {
    if (str.length() < suffix.length()) {
        return false;
    }
    return str.rfind(suffix) == (str.length() - suffix.length());
}

int main(int argc, char* argv[]) {
    for (int i = 0; i < argc; ++i) {
        std::cout << "Argument " << i << ": " << argv[i] << std::endl;
    }

    std::string path = "/";
    if (char* cwd = getcwd(nullptr, 0)) {
        path = cwd;
        free(cwd); // getcwd分配了内存，所以需要释放
    } else {
        throw std::runtime_error("Failed to get current directory");
    }
    int top=30;
    for(int i=0;i<argc;i++){
        std::string arg = argv[i];
        if(arg=="-D" && i+1<argc){
            path = argv[i+1];
        }
        else if(arg=="-T" && i+1<argc){
            top = std::stoi(argv[i+1]);
        }
    }
    try {
        DIR *dir;
        struct dirent *ent;
        if ((dir = opendir(path.c_str())) != nullptr) {
            closedir(dir);
            vector<FolderSize *> allDirs = vector<FolderSize *>();
            vector<FolderSize *> currentLevel = vector<FolderSize *>();
            FolderSize *root = new FolderSize(path, 0, 0, false);
            currentLevel.push_back(root);
            allDirs.push_back(root);
            int found = 0;
            int currentDepth = 0;
            while (currentLevel.size() > 0) {
                currentDepth += 1;
                vector<FolderSize *> nextLevel = vector<FolderSize *>();
                for (FolderSize *folderSize: currentLevel) {
                    string currentPath = (*folderSize).path;
                    try {
                        if ((dir = opendir(currentPath.c_str())) != nullptr) {
                            while ((ent = readdir(dir)) != nullptr) {
                                std::string subPath = currentPath + "/" + ent->d_name;
                                if(ends_with(currentPath,"/")){
                                    subPath = currentPath + ent->d_name;
                                }
                                if (ent->d_type == DT_DIR) { // 如果是目录
                                    if (std::string(ent->d_name) != "." && std::string(ent->d_name) != ".." &&
                                            subPath != "/proc") {
                                        found += 1;
                                        FolderSize *subFolderSize = new FolderSize(subPath, 0, currentDepth, false);
                                        nextLevel.push_back(subFolderSize);
                                        allDirs.push_back(subFolderSize);
                                        (*(*folderSize).subFolders).push_back(subFolderSize);
                                        std::cout << "找到子文件夹个数: " + to_string(found) << std::endl;
                                    }
                                }
                            }
                            closedir(dir);
                        } else {
                            std::cerr << "Unable to open directory: " << currentPath << std::endl;
                        }

                    }
                    catch (const system_error &e) {
                        std::cerr << e.what() << std::endl;
                    }
                }
                currentLevel.swap(nextLevel);
            }

            sort(allDirs.begin(), allDirs.end(), compareDepth);

            int calced = 0;
            for (FolderSize *folderSize: allDirs) {
                calcDirSize(folderSize);
                calced += 1;
                std::cout << "当前进度:" + to_string(calced) + "/" + to_string(allDirs.size()) << std::endl;
            }

            sort(allDirs.begin(), allDirs.end(), compareSize);

            for (int i = 0; i < top; i++) {
                if (i < allDirs.size()) {
                    FolderSize *folderSize = allDirs[i];
                    std::cout << (*folderSize).path + " " + presentSize((*folderSize).size) << std::endl;
                }
            }

            for (FolderSize *folderSize: allDirs) {
                delete (*folderSize).subFolders;
                delete folderSize;
            }

        } else {
            std::cerr << "Path does not exist or is not a directory." << std::endl;
        }
    }
    catch (const system_error &e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}


